//
// WinPcap.net
//
// Copyright (C) 2005 Matthew Ward
//
// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
//
// Matthew Ward (mrward@users.sourceforge.net)


using System;
using System.Diagnostics;
using System.Globalization;
using System.Text;

namespace WinPcap
{
	/// <summary>
	/// An Ethernet network data packet.
	/// </summary>
	public sealed class EthernetPacket : Packet
	{
		/// <summary>
		/// The size in bytes of the ethernet header.
		/// </summary>
		public const int HeaderLength = 14;

		/// <summary>
		/// The size in bytes of the ethernet MAC address.
		/// </summary>
		public const int AddressLength = 6;

		/// <summary>
		/// The size in bytes of the ethernet packet type.
		/// </summary>
		public const int PacketTypeLength = 2;

		/// <summary>
		/// The ethernet packet type byte index position in the header.
		/// </summary>
		public const int PacketTypePosition = AddressLength * 2;

		/// <summary>
		/// The packet destination byte index position in the header.
		/// </summary>
		public const int DestinationAddressPosition = 0;

		/// <summary>
		/// The packet source byte position in the header.
		/// </summary>
		public const int SourceAddressPosition = 
			DestinationAddressPosition + AddressLength;

		/// <summary>
		/// The packet's destination MAC address.
		/// </summary>
		string m_Destination = String.Empty;

		/// <summary>
		/// The packet's source MAC address.
		/// </summary>
		string m_Source = String.Empty;

		/// <summary>
		/// The packet type.
		/// </summary>
		EthernetPacketType m_PacketType = EthernetPacketType.None;
		
		/// <summary>
		/// The packet contained inside this one.
		/// </summary>
		Packet m_InnerPacket;

		/// <summary>
		/// Creates a new instance of this class.
		/// </summary>
		/// <param name="bytes">The packet's bytes.</param>
		public EthernetPacket( byte[ ] bytes )
		: base( bytes )
		{
			ParsePacket( bytes );
		}

		/// <summary>
		/// Gets the packet's destination.
		/// </summary>
		/// <returns>An ethernet MAC address.
		/// </returns>
		public string Destination
		{
			get
			{
				return m_Destination;
			}
		}

		/// <summary>
		/// Gets the packet's source.
		/// </summary>
		/// <returns>An ethernet MAC address.
		/// </returns>
		public string Source
		{
			get
			{
				return m_Source;
			}
		}

		/// <summary>
		/// Gets the ethernet packet type.
		/// </summary>
		public EthernetPacketType PacketType
		{
			get
			{
				return m_PacketType;
			}
		}

		/// <summary>
		/// Gets the packet contained inside this one.
		/// </summary>
		public override Packet InnerPacket
		{
			get
			{
				return GetInnerPacket( );
			}
		}

		/// <summary>
		/// Parses the ethernet packet.
		/// </summary>
		/// <param name="bytes">The packet's bytes.</param>
		void ParsePacket( byte[ ] bytes )
		{
			// Ensure packet is big enough.

			if( bytes.Length >= EthernetPacket.HeaderLength )
			{
				GetPacketType( bytes );
				m_Destination = GetAddress( bytes, EthernetPacket.DestinationAddressPosition );
				m_Source = GetAddress( bytes, EthernetPacket.SourceAddressPosition );
			}
		}

		/// <summary>
		/// Gets the packet type from the ethernet header.
		/// </summary>
		/// <param name="bytes">The packet's bytes.</param>
		void GetPacketType( byte[ ] bytes )
		{
			UInt16 PacketType = Packet.GetUInt16( bytes, 
				EthernetPacket.PacketTypePosition );
	
			m_PacketType = ( EthernetPacketType )PacketType;
		}

		/// <summary>
		/// Gets the ethernet MAC address from the byte array.
		/// </summary>
		/// <param name="bytes">The byte array.</param>
		/// <param name="startIndex">The index into the byte array
		/// that the address will be extracted from.</param>
		/// <returns>The ethernet MAC address.</returns>
		static string GetAddress( byte[ ] bytes, int startIndex )
		{
			Debug.Assert( bytes.Length >= ( startIndex + EthernetPacket.AddressLength ),
				"Not enough bytes to extract address from." );
	
			StringBuilder Address = new StringBuilder( );
	
			int BytesAdded = 0;
	
			for( int i = 0; i < EthernetPacket.AddressLength; ++i )
			{
				Byte CurrentByte = bytes[ i + startIndex ];
	
				Address.Append( CurrentByte.ToString( "X2", CultureInfo.InvariantCulture ) );
	
				++BytesAdded;
	
				if( BytesAdded < EthernetPacket.AddressLength )
				{
					Address.Append( ":" );
				}
			}
	
			return Address.ToString( );
		}
		
		/// <summary>
		/// Gets the packet contained inside this one.
		/// </summary>
		Packet GetInnerPacket( )
		{
			if( m_InnerPacket == null )
			{
				if( m_PacketType == EthernetPacketType.IP )
				{
					byte[ ] PacketBytes = GetBytes( );
	
					if( PacketBytes.Length > EthernetPacket.HeaderLength )
					{
						// Packet contains a payload.
	
						m_InnerPacket = new IPPacket( PacketBytes, 
							EthernetPacket.HeaderLength );
					}
				}
			}
	
			return m_InnerPacket;
		}
	}
}
